package jtg.generator;

import soot.*;

import java.lang.reflect.Field;
import java.util.*;

public class DealSolution {
    public static String dealSolution(String solution, List<Local> localPara) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//        if (solution.contains("\"")) {
//            boolean dealt = false;
//            String[] items = solution.split("\\s+");
//            for (String item : items) {
//                if (item.contains("\"") && item.contains("_value_len")) {
//                    int firstIndex = item.indexOf('\"');
//                    int lastIndex = item.lastIndexOf('\"');
//                    int len = item.substring(firstIndex, lastIndex + 1).length();
//                    solution.replace(item, String.valueOf(len));
//                    dealt = true;
//                }
//            }
//            if (dealt == false) throw new ExpressionException("无法处理");
//        }
        // 获取每个输入（都是空格隔开的
        String[] items = solution.split("\\s+"); //一行数据
        // 存=左右两侧
        Map<String, String> valueMap = new HashMap<>();
        for (String item : items) {
            String[] split = item.split("=");
            String compVar = split[0]; //r0_ind3_age=1
            String val = split[1];
            valueMap.put(compVar, val);
        }
        StringBuffer testInput = new StringBuffer();
        // 遍历所有参数,针对他原本的类型进行操作
        for (int i = 0; i < localPara.size(); i++) {
            Local para = localPara.get(i);
            String paraStr = para.toString();
            Type type = para.getType();
            if (type instanceof IntType) {
                // 因为z3把int也当成real处理的，里面进行了一些强制类型转换，可能会生成实际上是real的数字
                String res = Optional.ofNullable(valueMap.get(paraStr)).orElse("0");
                if (res.contains("/")) {
                    int index = res.indexOf('/');
                    int op1 = Integer.parseInt(res.substring(0, index));
                    int op2 = Integer.parseInt(res.substring(index+1, res.length()));
                    testInput.append((int)(op1 / op2) + " ");
                }
                else
                    testInput.append(Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr)).orElse("0")) + " ");
            } else if (type instanceof CharType) {
                testInput.append((char) Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr)).orElse("0")) + " ");
            } else if (type instanceof BooleanType) {
                testInput.append(Optional.ofNullable(valueMap.get(paraStr)).orElse("0").equals("0") + " ");//0t 1f
            } else if (type instanceof ByteType) {
                testInput.append(Optional.ofNullable(valueMap.get(paraStr)).orElse(paraStr) + " ");
            } else if (type.toString().equals("java.lang.String")) {
                // 暂时只处理了有关字符串的length的
                // TODO: 只处理了length函数
                int len = Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr + "_value_len")).orElse("1"));
                testInput.append(RandomGenerator.ranStr(len) + " ");
            } else if (type instanceof ArrayType) {
                ArrayType arrayType = (ArrayType) type;
                // 表示数组的字符串: (xx=xx;xx=xx;..)
                StringBuffer arrayStr = new StringBuffer();
                // 数组的长度，前面已经加上限制了，如果没有，那就设1
                int arrLen = Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr + "_len")).orElse("1"));
                // 数组每个元素的类型，根据这个类型来生成具体的数据
                Type arrEleType = arrayType.getArrayElementType();
                for (int j = 0; j < arrLen; j++) {
                    if (0 == j) arrayStr.append("[");
                    // 每个元素
                    String element = paraStr + "_i" + j;
                    arrayStr.append(generateArray(element, arrEleType, valueMap) + ";");
                    if (j == arrLen-1) arrayStr.append("]");
                }
                testInput.append(arrayStr + " ");
            } else if (type instanceof RefType) {
                // 如果是引用的某个类或者接口
                Class<?> cls = Class.forName(type.toString());
                if (cls.isEnum()) {
                    // 如果是枚举类型
                    Class<Enum> enumClass = (Class<Enum>) cls;
                    Enum[] enumCons = enumClass.getEnumConstants();
                    // 分情况，参数可能就是枚举类型的某个具体值
//                    if (type instanceof )
                    testInput.append(enumCons[Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr)).orElse("0"))] + " ");
                } else {
                    Field[] fields = cls.getFields();
                    Object obj = cls.newInstance();
                    // 如果引用的是类中的某个字段,就命名为:类_字段
                    for (Field field : fields) {
                        Class<?> fieldType = field.getType();
                        if (fieldType == Integer.class || fieldType == int.class) {
                            field.setInt(obj,Integer.parseInt(Optional.ofNullable(valueMap.get(paraStr + "_" + field.getName())).orElse("0")));
                        } else if (fieldType == String.class) {
                            field.set(obj, Optional.ofNullable(valueMap.get(paraStr + "_" + field.getName())).orElse(""));
                        } else if (fieldType == float.class || fieldType == Float.class){
                            field.set(obj, Float.parseFloat(Optional.ofNullable(valueMap.get(paraStr + "_" + field.getName())).orElse("0.0")));
                        } else {
                            field.set(obj, null);
                        }
                    }
                    testInput.append(obj + " ");
                }
            } else{
            }
        }
        return testInput.toString();
    }

    private static String generateArray(String element, Type arrEleType, Map<String, String> valueMap) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        StringBuffer arrEleStr = new StringBuffer();
        if (arrEleType instanceof IntType) {
            // 因为z3把int也当成real处理的，里面进行了一些强制类型转换，可能会生成实际上是real的数字
            String res = Optional.ofNullable(valueMap.get(element)).orElse("0");
            if (res.contains("/")) {
                int index = res.indexOf('/');
                int op1 = Integer.parseInt(res.substring(0, index));
                int op2 = Integer.parseInt(res.substring(index+1, res.length()));
                arrEleStr.append((int)(op1 / op2));
            }
            else
                arrEleStr.append(Integer.parseInt(Optional.ofNullable(valueMap.get(element)).orElse("0")));
//            arrEleStr.append(Integer.parseInt(Optional.ofNullable(valueMap.get(element)).orElse("0")));
        } else if (arrEleType instanceof CharType) {
            arrEleStr.append((char) Integer.parseInt(Optional.ofNullable(valueMap.get(element)).orElse("0")));
        } else if (arrEleType instanceof BooleanType) {
            arrEleStr.append(Optional.ofNullable(valueMap.get(element)).orElse("0").equals("0"));//0t 1f
        } else if (arrEleType.toString().equals("java.lang.String")) {
            // 暂时只处理了有关字符串的length的
            // TODO: 只处理了length函数
            int len = Integer.parseInt(Optional.ofNullable(valueMap.get(element + "_value_len")).orElse("1"));
            arrEleStr.append(RandomGenerator.ranStr(len));
        } else if (arrEleType instanceof ArrayType) {
            // 递归类型，不知道写的对不对
            ArrayType arrayType = (ArrayType) arrEleType;
            // 表示数组的字符串: (xx=xx;xx=xx;..)
            StringBuffer arrayStr = new StringBuffer();
            // 数组的长度，前面已经加上限制了，如果没有，那就设1
            int arrLen = Integer.parseInt(Optional.ofNullable(valueMap.get(element + "_len")).orElse("1"));
            // 数组每个元素的类型，根据这个类型来生成具体的数据
            Type arrEleEleType = arrayType.getArrayElementType();
            for (int j = 0; j < arrLen; j++) {
                if (0 == j) arrayStr.append("[");
                // 每个元素
                String eleElement = element + "_i" + j;
                arrayStr.append(generateArray(eleElement, arrEleEleType, valueMap) + ";");
                if (j == arrLen-1) arrayStr.append("]");
            }
            arrEleStr.append(arrayStr);
        } else if (arrEleType instanceof RefType) {
            // 如果是引用的某个类或者接口
            Class<?> cls = Class.forName(arrEleType.toString());
            if (cls.isEnum()) {
                // 如果是枚举类型
                Class<Enum> enumClass = (Class<Enum>) cls;
                Enum[] enumCons = enumClass.getEnumConstants();
                arrEleStr.append(enumCons[Integer.parseInt(Optional.ofNullable(valueMap.get(element)).orElse("0"))]);
            } else {
                Field[] fields = cls.getFields();
                Object obj = cls.newInstance();
                // 如果引用的是类中的某个字段,就命名为:类_字段
                for (Field field : fields) {
                    Class<?> fieldType = field.getType();
                    if (fieldType == Integer.class || fieldType == int.class) {
                        field.setInt(obj,Integer.parseInt(Optional.ofNullable(valueMap.get(element + "_" + field.getName())).orElse("0")));
                    } else if (fieldType == String.class) {
                        field.set(obj, Optional.ofNullable(valueMap.get(element + "_" + field.getName())).orElse(""));
                    } else {
                        field.set(obj, null);
                    }
                }
                arrEleStr.append(obj);
            }
        }else{
        }
        return arrEleStr.toString();
    }

    // 生成对应Object而不是String的，只针对一个参数的方法！
    public static List<Object> dealSolution2Object(List<String> testSet, List<Local> localPara) throws Exception {
        if (localPara.size() > 1) throw new Exception("抱一丝，我们只能针对一个参数的方法生成junit代码");
        List<Object> testSetObj = new ArrayList<>();
        for (String aTestinput : testSet) {
            // 获取每个输入（都是空格隔开的
            String[] items = aTestinput.split("\\s+"); //一行数据
            if (items.length > 1) throw new Exception("抱一丝，我们只能针对一个参数的方法生成junit代码");
            // 存=左右两侧
            Map<String, String> valueMap = new HashMap<>();
            String[] split = aTestinput.split("=");
            valueMap.put(split[0], split[1].trim());

            Local param = localPara.get(0);
            if (param.getType() instanceof ArrayType) {
                ArrayType arrayType = (ArrayType) param.getType();
                int len = Integer.parseInt(Optional.ofNullable(valueMap.get(param + "_len")).orElse("1")); //其实嘛也不是一定存在len
                Object[] arr = new Object[len];
                Type arrayElementType = arrayType.getArrayElementType();
                for (int j = 0; j < arr.length; j++) {
                    String val = param + "_i" + j;
                    arr[j] = dealSimpleType(arrayElementType, val, valueMap);
                }
                testSetObj.add(arr);
            }else{
                testSetObj.add(dealSimpleType(param.getType(), param.toString(), valueMap));
            }

        }
        return testSetObj;
    }

    // 处理简单类型的测试用例
    private static Object dealSimpleType(Type type, String val, Map<String, String> valueMap) throws Exception {
        if (type instanceof IntType) {
            // 因为z3把int也当成real处理的，里面进行了一些强制类型转换，可能会生成实际上是real的数字
            String res = Optional.ofNullable(valueMap.get(val)).orElse("0");
            if (res.contains("/")) {
                int index = res.indexOf('/');
                int op1 = Integer.parseInt(res.substring(0, index));
                int op2 = Integer.parseInt(res.substring(index+1, res.length()));
                return (int)(op1 / op2);
            }
            else
                return Integer.parseInt(Optional.ofNullable(valueMap.get(val)).orElse("0"));
//            return Integer.parseInt(Optional.ofNullable(valueMap.get(val)).orElse("0"));
        } else if (type instanceof CharType) {
            return (char) Integer.parseInt(Optional.ofNullable(valueMap.get(val)).orElse("0"));
        } else if (type instanceof BooleanType) {
            return Optional.ofNullable(valueMap.get(val)).orElse("0").equals("0"); //0t 1f
        } else if (type.toString().equals("java.lang.String")) {
            int len = Integer.parseInt(Optional.ofNullable(valueMap.get(val + "_value_len")).orElse("1"));
            return RandomGenerator.ranStr(len);
        } else if (type instanceof RefType) {
            Class<?> clz = Class.forName(type.toString());
            if (clz.isEnum()) {
                Class<Enum> clazz = (Class<Enum>) clz;
                Enum[] enumConstants = clazz.getEnumConstants();
                return enumConstants[Integer.parseInt(Optional.ofNullable(valueMap.get(val)).orElse("0"))];
            } else {
                Field[] fields = clz.getFields();
                Object obj = clz.newInstance();
                for (Field field : fields) {
                    Class<?> fieldType = field.getType();
                    if (fieldType == Integer.class || fieldType == int.class) {
                        field.setInt(obj,Integer.parseInt(Optional.ofNullable(valueMap.get(val + "_" + field.getName())).orElse("0")));
                    } else if (fieldType == String.class) {
                        field.set(obj, Optional.ofNullable(valueMap.get(val + "_" + field.getName())).orElse(""));
                    } else {
                        field.set(obj, null);
                    }
                }
                return obj;
            }
        }else{
            return null;
        }
    }
}
